Market basket

Market basket análsis

Notebook por Alfredo Pasmiño

Secciones

1. Problema

El siguiente análisis corresponde a las cotizaciones y facturaciones de repuestos en SKC y como se comportan estas en relación a los ítems que poseen, específicamanete a que grupo de productos estan involucrados y como se comporta cada uno en los dos diferentes escenarios cotizado y facturado. La principal idea es determinar que productos finalmente se dejan fuera al momento de la factura e identificarlos para en un posterior análisis buscar la causa de la razón por la cuál desaparecen de la compra final. El análisis se basa principalmente por grupo de productos ya que por artículo considerando el negocio de repuestos no entrega una visión clara ya que muchos repuestos solo se venden una vez para casos particulares a diferencia de los ítems que se venden con frecuencia para mantenciones, las reparaciones son diferentes e involucran items que probablemente se utilicen en solo para esa ocasión.

In [28]:
library("arules")
library("plyr")
library("ggplot2")
library("knitr")
library("Matrix")
library("dplyr")
library("arulesViz")
library("repr")

2. Análisis exploratorio

Seleccionamos el directorio donde estan los archivos .txt y los leemos con la función read.csv. Para ello utilizaremos dos archivos uno con las cotizaciones y otro con las facturas.

In [2]:
setwd("C:/Users/alfredo/Documents")
In [3]:
df_cot<-read.csv(file="cotizado.txt", header=T, sep="\t", dec="," )
df_ven<-read.csv(file="ventas.txt", header=T, sep="\t", dec="," )

Revisamos las primeras cinco filas de las cotizaciones y facturas.

In [4]:
head(df_cot,5)
Doc_vtasPosCreado_elMESSolicNombreGrM1MaterialTexto_materialLinea...Total_Vta_S.DPrecio_Vta_C.DTotal_Vta_C.DtotalingresoCosto_UnitarioTotal_CostoPrecio.Compra.C.DProduct.groupDESCRIPCION
1301350232 10 01.04.2016 01-04-2016 0:00 210289 Servicios Forestales Y Comerciales. A VOE21707133 FILTRO ACEITE REPUESTOS MAQUINARIA MCF ... 63260 10754 43017 43017 71.70 8 30 0 717 PIEZAS FILTRO
1301350232 20 01.04.2016 01-04-2016 0:00 210289 Servicios Forestales Y Comerciales. A VOE21707132 FILTRO ACEITE REPUESTOS MAQUINARIA MCF ... 102060 17350 69401 69401 115.67 9 37 0 717 PIEZAS FILTRO
1301350232 40 01.04.2016 01-04-2016 0:00 210289 Servicios Forestales Y Comerciales. A VOE11110683 FILTRO SEPARADOR AGUA REPUESTOS MAQUINARIA MCF ... 1700 552 1105 110400 184.00 339 678 0 717 PIEZAS FILTRO
1301350232 80 01.04.2016 01-04-2016 0:00 210289 Servicios Forestales Y Comerciales. A VOE11037868 FILTRO HIDRAULICO Y TRANSMISIO REPUESTOS MAQUINARIA MCF ... 2790 949 1897 189700 316.17 471 941 0 717 PIEZAS FILTRO
1301350232 30 01.04.2016 01-04-2016 0:00 210289 Servicios Forestales Y Comerciales. A VOE15126069 FILTRO COMBUSTIBLE REPUESTOS MAQUINARIA MCF ... 1352 230 919 91900 153.17 98 390 NA 717 PIEZAS FILTRO
In [5]:
head(df_ven, 5)
documentoarticulodescripcionpgpg_descripcioncantidadingreso
3201835521 VOE11988582 RETEN 792 VARIADOS, SEALINGS Y JUNTAS1 87.31
3201837655 VOE983505 ANILLO TORICO 792 VARIADOS, SEALINGS Y JUNTAS5 9.04
3201837655 VOE983502 O¦RING 792 VARIADOS, SEALINGS Y JUNTAS1 1.25
3201838786 VOE20800016 V-BELT PULLEY 702 PIEZAS DE TRANSMISION 1 630.54
3201838786 VOE21901696 EJE PERFORADO 702 PIEZAS DE TRANSMISION 1 46.12

Revisamos el total de filas y columnas para las cotizaciones y facturas.

In [6]:
dim(df_cot)
  1. 29116
  2. 26
In [7]:
dim(df_ven)
  1. 12634
  2. 7

Revisamos si los dataframes contienen datos missing.

In [8]:
sum(is.na(df_cot[c('Cantidad', 'total')]))
2
In [9]:
sum(is.na(df_ven[c('cantidad', 'ingreso')]))
0

Tenemos 2 datos missing en las cotizaciones, para ello no utilizaremos estos datos y procederemos a eliminarlos del dataframe.

In [10]:
df_cot<-na.omit(df_cot)

Realizamos un summary para revisar los estadísticos básicos de cada dataframe.

In [11]:
summary(subset(df_cot, select=c(Cantidad, ingreso)))
    Cantidad          ingreso         
 Min.   :  1.000   Min.   :     0.00  
 1st Qu.:  1.000   1st Qu.:    30.07  
 Median :  1.000   Median :    95.08  
 Mean   :  3.386   Mean   :   492.45  
 3rd Qu.:  2.000   3rd Qu.:   294.57  
 Max.   :784.000   Max.   :209515.17  

Para las cotizaciones tenemos que la mínima cantidad cotizada es de 0 ítem con una media de 3.386, una mediana de 1 y el máximo es de 784 unidades. Para los montos cotizados el mínimo es de 0 USD lo cual representa un outlier, la media es de 492,45 USD y el monto máximo es de 209.515 USD.

In [12]:
summary(subset(df_ven, select=c(cantidad, ingreso)))
    cantidad          ingreso        
 Min.   : -1.000   Min.   : -314.20  
 1st Qu.:  1.000   1st Qu.:   20.08  
 Median :  1.000   Median :   71.78  
 Mean   :  3.198   Mean   :  218.04  
 3rd Qu.:  3.000   3rd Qu.:  195.10  
 Max.   :784.000   Max.   :14779.34  

Para la facturación tenemos que la mínima cantidad facturada es de -1 ítem (probablemente se debe a una devolución) con una media de 3.198, una mediana de 1 y el máximo es de 784 unidades. Para los montos facturados el mínimo es de -314 USD lo cual representa una nota de crédito, la media es de 218,04 USD y el monto máximo es de 14.779 USD.

Gráficamos la cantidad de items por cotizaciones.

In [13]:
options(repr.plot.width=7, repr.plot.height=3)

df_cot %>% 
  group_by(Doc_vtas) %>% 
  summarize(n_items = sum(Cantidad)) %>%
  ggplot(aes(x=n_items))+
  geom_histogram(fill="indianred", bins = 300) + 
  labs(title="ítems por cotizaciones") +
  geom_rug()+
  coord_cartesian(xlim=c(0,50))

La mayoría de los clientes cotiza menos de 10 ítems por cada cotización emitida ya que existen mas de 4.000 cotizaciones con ese rango.

In [14]:
options(repr.plot.width=7, repr.plot.height=3)

df_ven %>% 
  group_by(documento) %>% 
  summarize(n_items = sum(cantidad)) %>%
  ggplot(aes(x=n_items))+
  geom_histogram(fill="indianred", bins = 300) + 
  labs(title="ítems por facturas") +
  geom_rug()+
  coord_cartesian(xlim=c(0,25))

En su mayoría las facturas contienen menos de 5 ítems esto corresponde a mas de 1.000 documentos facturados.

In [15]:
options(repr.plot.width=8, repr.plot.height=6)
theme_set(theme_bw())

df_cot %>% 
  group_by(DESCRIPCION) %>% 
  summarize(n_items = sum(Cantidad)) %>%
  arrange(desc(n_items)) %>%
  head(20) %>%

ggplot(aes(x=DESCRIPCION, y=n_items )) + 
  geom_bar(stat="identity", width=.5, fill="tomato3") + 
  labs(title="Cantidad ítems cotizados",  
       caption="source: Cotizaciones") + 
       geom_text(aes(label=n_items), position=position_dodge(width=0.9), vjust=-0.25)+
 theme(axis.text.x = element_text(angle=65, vjust=0.6))

Revisando por grupo de artículo, las principales cantidades se encuentran en la categoría, piezas de cerradura, piezas filtro, variados, sellos y juntas y variados partes de metal.

In [16]:
theme_set(theme_bw())

df_ven %>% 
  group_by(pg_descripcion) %>% 
  summarize(n_items = sum(cantidad)) %>%
  arrange(desc(n_items)) %>%
  head(20) %>%

ggplot(aes(x=pg_descripcion, y=n_items)) + 
  geom_bar(stat="identity", width=.5, fill="tomato3") + 
  labs(title="Cantidad ítems cotizados",  
       caption="source: Cotizaciones") + 
geom_text(aes(label=n_items), position=position_dodge(width=0.9), vjust=-0.25)+
theme(axis.text.x = element_text(angle=65, vjust=0.6))

Las principales cantidades por facturación se encuentra en las piezas de cerradura, piezas filtro, variados, sellos y juntas y variados y piezas de metal. Las categorías tienen un cierto comportamiento similar a las cotizaciones para ello se revisara la tasa de cierre que corresponde a la razón entre los ítems facturados y los ítems cotizados.

In [17]:
df1<-df_cot %>% 
  group_by(DESCRIPCION) %>% 
  summarize(n_items = sum(Cantidad)) %>%
  arrange(desc(n_items))

df2<-df_ven %>% 
  group_by(pg_descripcion) %>% 
  summarize(n_items = sum(cantidad)) %>%
  arrange(desc(n_items)) 

df_ts<-merge(df1, df2, by.x='DESCRIPCION', by.y='pg_descripcion')
In [18]:
options(repr.plot.width=8, repr.plot.height=6)

df_ts %>% 
  mutate(perc = round(n_items.y / n_items.x,2 )) %>%
  head(32) %>%
 ggplot(aes(x = DESCRIPCION, y = perc)) + 
 geom_bar(stat="identity", width=.5, fill="tomato3") +
 labs(title="Tasa cierre")+
 geom_text(aes(label=perc), position=position_dodge(width=0.9), vjust=-0.25)+
 theme(axis.text.x = element_text(angle=65, vjust=0.6))

Al revisar la tasa de cierre de los principales grupos el grupo de cerradura tiene un cierre de un 37%, las piezas de filtro un 81% y los variados sellos y junta un 52%, existen otros grupos con una muy baja tasa como las baterias, piezas marco, rodados y baterias debido a la fuerte competencia en el sector y la existencia de muchas opciones alternativas a diferencia de los que tienen una tasa alta como las piezas de filtro, aceites, piezas elétricas y piezas de desgaste que son mas exclusivas de la marca. Este análisis permite identificar a que grupos que se podría asignar un precio más competitivo y tratar los de zona entre un 30% y 50% de identificar cada material para ver su posterior tasa de cierre individual.

3. Reglas de asociación

Usaremos el algoritmo apriori para minar los grupos de items frecuentes y reglas de asociación.

Usaremos como soporte=0.005 y confianza =0.8 para devolver todas las reglas que tienen un soporte de al menos un 0.5% y una confianza de al menos el 80%.

In [21]:
tr_cot<-read.transactions(file="cotizado2.txt", rm.duplicates=TRUE, format="single", sep="\t", col=c(1,2))
rules_cot <- apriori(tr_cot, parameter=list(support=0.005, confidence=0.8))
rules_cot <- sort(rules_cot, by='confidence', decreasing = TRUE)
summary(rules_cot)
Apriori

Parameter specification:
 confidence minval smax arem  aval originalSupport maxtime support minlen
        0.8    0.1    1 none FALSE            TRUE       5   0.005      1
 maxlen target   ext
     10  rules FALSE

Algorithmic control:
 filter tree heap memopt load sort verbose
    0.1 TRUE TRUE  FALSE TRUE    2    TRUE

Absolute minimum support count: 31 

set item appearances ...[0 item(s)] done [0.00s].
set transactions ...[35 item(s), 6257 transaction(s)] done [0.00s].
sorting and recoding items ... [29 item(s)] done [0.00s].
creating transaction tree ... done [0.00s].
checking subsets of size 1 2 3 4 5 6 done [0.00s].
writing ... [313 rule(s)] done [0.00s].
creating S4 object  ... done [0.00s].
set of 313 rules

rule length distribution (lhs + rhs):sizes
  3   4   5   6 
 33 158 102  20 

   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  3.000   4.000   4.000   4.348   5.000   6.000 

summary of quality measures:
    support           confidence          lift            count      
 Min.   :0.005114   Min.   :0.8000   Min.   : 3.227   Min.   : 32.0  
 1st Qu.:0.005434   1st Qu.:0.8261   1st Qu.: 4.495   1st Qu.: 34.0  
 Median :0.006073   Median :0.8571   Median : 5.423   Median : 38.0  
 Mean   :0.006792   Mean   :0.8672   Mean   : 5.741   Mean   : 42.5  
 3rd Qu.:0.007352   3rd Qu.:0.9038   3rd Qu.: 6.238   3rd Qu.: 46.0  
 Max.   :0.019019   Max.   :1.0000   Max.   :10.867   Max.   :119.0  

mining info:
   data ntransactions support confidence
 tr_cot          6257   0.005        0.8

El resumen de las reglas para las cotizacionesnos entrega la siguiente información:

  • El numero de reglas es 313
  • El resumen de la calidad de las medidas, el rango de soporte, confianza y lift.
  • El número de transacciones 6257 y su mínimo de parámetro que fueron ingresados anteriormente.
In [22]:
tr_ven<-read.transactions(file="ventas2.txt", rm.duplicates=TRUE, format="single", sep="\t", col=c(1,2))
rules_ven <- apriori(tr_ven, parameter=list(support=0.005, confidence=0.8))
rules_ven <- sort(rules_ven, by='confidence', decreasing = TRUE)
summary(rules_ven)
Apriori

Parameter specification:
 confidence minval smax arem  aval originalSupport maxtime support minlen
        0.8    0.1    1 none FALSE            TRUE       5   0.005      1
 maxlen target   ext
     10  rules FALSE

Algorithmic control:
 filter tree heap memopt load sort verbose
    0.1 TRUE TRUE  FALSE TRUE    2    TRUE

Absolute minimum support count: 55 

set item appearances ...[0 item(s)] done [0.00s].
set transactions ...[35 item(s), 11072 transaction(s)] done [0.00s].
sorting and recoding items ... [24 item(s)] done [0.00s].
creating transaction tree ... done [0.00s].
checking subsets of size 1 2 3 4 5 done [0.00s].
writing ... [20 rule(s)] done [0.00s].
creating S4 object  ... done [0.00s].
set of 20 rules

rule length distribution (lhs + rhs):sizes
 3  4 
 8 12 

   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    3.0     3.0     4.0     3.6     4.0     4.0 

summary of quality measures:
    support           confidence          lift           count      
 Min.   :0.005058   Min.   :0.8000   Min.   :2.990   Min.   :56.00  
 1st Qu.:0.005984   1st Qu.:0.8060   1st Qu.:3.696   1st Qu.:66.25  
 Median :0.006638   Median :0.8266   Median :3.889   Median :73.50  
 Mean   :0.006562   Mean   :0.8384   Mean   :4.301   Mean   :72.65  
 3rd Qu.:0.007000   3rd Qu.:0.8638   3rd Qu.:5.286   3rd Qu.:77.50  
 Max.   :0.008761   Max.   :0.9200   Max.   :5.878   Max.   :97.00  

mining info:
   data ntransactions support confidence
 tr_ven         11072   0.005        0.8

EL resultado de las reglas para las facturas es diferente ya que con los mismos parámetros de ingreso de 313 reglas pasamos a solo 20, significa que muchas piezas cotizadas el cliente solo compra una parte (las más dificiles de conseguir) y la otras las busca en otro lado ya que puede ser por problema de stock o precio.

Las 10 principales reglas son:

In [23]:
inspect(rules_cot[1:10])
     lhs                              rhs                               support confidence     lift count
[1]  {BUJES,                                                                                             
      PIEZAS CERRADURA,                                                                                  
      PIEZAS DE TRANSMISION,                                                                             
      VARIADOS, NO METÁLICOS,}     => {VARIADOS, SEALINGS Y JUNTAS} 0.005593735  1.0000000 4.494971    35
[2]  {PIEZAS CERRADURA,                                                                                  
      PIEZAS DE TRANSMISION,                                                                             
      PIEZAS HIDRAULICAS,                                                                                
      VARIADOS, NO METÁLICOS,}     => {VARIADOS, METAL PARTES}      0.005274093  1.0000000 9.352765    33
[3]  {BUJES,                                                                                             
      PIEZAS CERRADURA,                                                                                  
      PIEZAS HIDRAULICAS,                                                                                
      VARIADOS, NO METÁLICOS,}     => {VARIADOS, SEALINGS Y JUNTAS} 0.007351766  1.0000000 4.494971    46
[4]  {BUJES,                                                                                             
      PIEZAS CERRADURA,                                                                                  
      PIEZAS DE TRANSMISION,                                                                             
      VARIADOS, METAL PARTES,                                                                            
      VARIADOS, NO METÁLICOS,}     => {VARIADOS, SEALINGS Y JUNTAS} 0.005114272  1.0000000 4.494971    32
[5]  {BUJES,                                                                                             
      PIEZAS CERRADURA,                                                                                  
      PIEZAS HIDRAULICAS,                                                                                
      VARIADOS, METAL PARTES,                                                                            
      VARIADOS, NO METÁLICOS,}     => {VARIADOS, SEALINGS Y JUNTAS} 0.006712482  1.0000000 4.494971    42
[6]  {BUJES,                                                                                             
      PIEZAS CERRADURA,                                                                                  
      VARIADOS, NO METÁLICOS,}     => {VARIADOS, SEALINGS Y JUNTAS} 0.009908902  0.9841270 4.423623    62
[7]  {BUJES,                                                                                             
      PIEZAS HIDRAULICAS,                                                                                
      VARIADOS, NO METÁLICOS,}     => {VARIADOS, SEALINGS Y JUNTAS} 0.007831229  0.9800000 4.405072    49
[8]  {BUJES,                                                                                             
      PIEZAS CERRADURA,                                                                                  
      VARIADOS, METAL PARTES,                                                                            
      VARIADOS, NO METÁLICOS,}     => {VARIADOS, SEALINGS Y JUNTAS} 0.007671408  0.9795918 4.403237    48
[9]  {BUJES,                                                                                             
      PIEZAS HIDRAULICAS,                                                                                
      VARIADOS, METAL PARTES,                                                                            
      VARIADOS, NO METÁLICOS,}     => {VARIADOS, SEALINGS Y JUNTAS} 0.006872303  0.9772727 4.392813    43
[10] {BUJES,                                                                                             
      PIEZAS HIDRAULICAS,                                                                                
      VARIADOS, METAL PARTES,                                                                            
      VARIADOS, NO METÁLICOS,,                                                                           
      VARIADOS, SEALINGS Y JUNTAS} => {PIEZAS CERRADURA}            0.006712482  0.9767442 6.474034    42
In [24]:
inspect(rules_ven[1:10])
     lhs                                        rhs                               support confidence     lift count
[1]  {ACEITES,                                                                                                     
      VARIADOS, SEALINGS Y JUNTAS}           => {PIEZAS FILTRO}               0.006231936  0.9200000 3.407909    69
[2]  {ACEITES,                                                                                                     
      PIEZAS CERRADURA}                      => {PIEZAS FILTRO}               0.006864162  0.9047619 3.351463    76
[3]  {VARIADOS, METAL PARTES,                                                                                      
      VARIADOS, NO METÁLICOS,,                                                                                     
      VARIADOS, SEALINGS Y JUNTAS}           => {PIEZAS CERRADURA}            0.006683526  0.9024390 5.877532    74
[4]  {VARIADOS, METAL PARTES,                                                                                      
      VARIADOS, NO METÁLICOS,}               => {PIEZAS CERRADURA}            0.007677023  0.8762887 5.707216    85
[5]  {PIEZAS CERRADURA,                                                                                            
      VARIADOS, METAL PARTES,                                                                                      
      VARIADOS, NO METÁLICOS,}               => {VARIADOS, SEALINGS Y JUNTAS} 0.006683526  0.8705882 4.012970    74
[6]  {PIEZAS CERRADURA,                                                                                            
      VARIADOS, METAL PARTES,                                                                                      
      VARIADOS, TUBOS, TUBERÍAS Y MANGUERAS} => {VARIADOS, SEALINGS Y JUNTAS} 0.005057803  0.8615385 3.971255    56
[7]  {VARIADOS, METAL PARTES,                                                                                      
      VARIADOS, NO METÁLICOS,}               => {VARIADOS, SEALINGS Y JUNTAS} 0.007406069  0.8453608 3.896684    82
[8]  {PIEZAS CERRADURA,                                                                                            
      PIEZAS HIDRAULICAS,                                                                                          
      VARIADOS, TUBOS, TUBERÍAS Y MANGUERAS} => {VARIADOS, SEALINGS Y JUNTAS} 0.005780347  0.8421053 3.881678    64
[9]  {VARIADOS, METAL PARTES,                                                                                      
      VARIADOS, TUBOS, TUBERÍAS Y MANGUERAS} => {VARIADOS, SEALINGS Y JUNTAS} 0.006593208  0.8390805 3.867735    73
[10] {PIEZAS CERRADURA,                                                                                            
      PIEZAS ELECTRICAS,                                                                                           
      VARIADOS, TUBOS, TUBERÍAS Y MANGUERAS} => {VARIADOS, SEALINGS Y JUNTAS} 0.005057803  0.8358209 3.852710    56

Revisando las reglas en la facturación se puede apreciar que figura los aceites, variados y sellos con filtros, esto basicamente representa las mantenciones a los equipos pero existe un contraste en comparación con las cotizaciones ya que muchas reglas se pierden y como algunas de estas que representan una reparación que incluye piezas de transmisión, cerradura, bujes y sellos, en la facturación solo aparece un subgrupo en la facturación y desaparece el grupo de transmisión.

In [27]:
options(repr.plot.width=8, repr.plot.height=8)
plot(rules_cot, method="grouped", measure=c("support", "confidence"), shading="lift", control=list(k=10))
In [83]:
plot(rules_ven, method="grouped", control=list(k=10))
In [84]:
options(repr.plot.width=6, repr.plot.height=4)
topRules_cot <- rules_cot[1:10];
plot(topRules_cot);
In [85]:
topRules_ven <- rules_ven[1:10];
plot(topRules_ven);
In [86]:
options(repr.plot.width=5, repr.plot.height=5)
plot(topRules_cot, method="graph");
In [87]:
options(repr.plot.width=5, repr.plot.height=5)
plot(topRules_ven, method="graph")

Las siguientes son representaciones gráficas del mismo efecto o como se pierden grupos en la facturación que inicialmente se cotizaban en la facturación. Se puede apreciar como el soporte que representa la cantidad de veces que aparece por factura o cotización pertenece al grupo de filtro, aceites, piezas de cuerpo y desgaste por igual en ambos tipos de documento representado por el tamaño del circulo. El lift representa cual es la probabilidad que estos productos se encuentren juntos representados por el color, mientras mas fuerte es el rojo los productos se encuentran juntos en cada documento así se puede apreciar a simple vista en las cotizaciones que su color es mucho mas intenso en comparación con la facturación final.

4. Conclusión

Podemos concluir que existen reglas que estan presentes en las cotizaciones pero que no necesariamente se están aplicando para la facturación, logrando identificarse los grupos de productos y cuales tienen este problema. Como este es el primer análisis el siguiente es identificar que artículos de los grupos que revisamos esta teniendo problema e investigar que puede estar ocurriendo ya que puede ser problema de stock, disponibilidad inmediata o precio.